package cluster

import (
	"fmt"
	"mariadb-operator/pkg/apis/finupgroup/v1alpha1"
	"mariadb-operator/pkg/constants"
	"mariadb-operator/pkg/resources/configmap"
	"mariadb-operator/pkg/resources/secret"
	"mariadb-operator/pkg/resources/service"
	"mariadb-operator/pkg/resources/statefulset"
	"mariadb-operator/pkg/tools/parse"

	appsv1 "k8s.io/api/apps/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/util/retry"

	"github.com/operator-framework/operator-sdk/pkg/k8sclient"
	"github.com/operator-framework/operator-sdk/pkg/sdk"
)

// Create create mariadb cluster
func Create(cluster *v1alpha1.MariaDBCluster) error {
	headless := service.New(cluster, cluster.Name, true)
	head := service.New(cluster, cluster.Name+"-read", false)
	srt := secret.New(cluster)
	ss := statefulset.New(cluster)

	if cluster.Spec.CMName == "" {
		cm := configmap.New(cluster)
		retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
			return sdk.Create(cm)
		})
		if retryErr != nil && !errors.IsAlreadyExists(retryErr) {
			return fmt.Errorf("failed to create mariadb cluster default configmap : %v", retryErr)
		}
	}

	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Create(headless)
	})
	if retryErr != nil && !errors.IsAlreadyExists(retryErr) {
		return fmt.Errorf("failed to create statefulset headless service : %v", retryErr)
	}

	retryErr = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Create(head)
	})
	if retryErr != nil && !errors.IsAlreadyExists(retryErr) {
		return fmt.Errorf("failed to create statefulset head service : %v", retryErr)
	}

	retryErr = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Create(srt)
	})
	if retryErr != nil && !errors.IsAlreadyExists(retryErr) {
		return fmt.Errorf("failed to create statefulset's secret : %v", retryErr)
	}

	retryErr = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Create(ss)
	})
	if retryErr != nil && !errors.IsAlreadyExists(retryErr) {
		return fmt.Errorf("failed to create statefulset : %v", retryErr)
	}

	return nil
}

// Update update mariadb cluster
func Update(cluster *v1alpha1.MariaDBCluster) error {
	oldss, _ := k8sclient.GetKubeClient().AppsV1().StatefulSets(cluster.Namespace).Get(cluster.Name, metav1.GetOptions{})

	if oldss.Spec.Replicas != &cluster.Spec.Replicas {
		oldss.Spec.Replicas = &cluster.Spec.Replicas
	}
	oldss.Annotations["cpu"] = cluster.Spec.Resources.Requests.Cpu().String()
	oldss.Annotations["memory"] = cluster.Spec.Resources.Requests.Memory().String()
	oldss.Spec.Template.Spec.Containers = statefulset.NewContainers(cluster)
	_, err := k8sclient.GetKubeClient().AppsV1().StatefulSets(cluster.Namespace).Update(oldss)
	return err
}

// Delete delete mariadb cluster
func Delete(cluster *v1alpha1.MariaDBCluster) error {
	headless := service.New(cluster, cluster.Name, true)
	head := service.New(cluster, cluster.Name+"-read", false)
	srt := secret.New(cluster)
	ss := statefulset.New(cluster)

	retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Delete(ss)
	})

	if retryErr != nil && !errors.IsNotFound(retryErr) {
		return fmt.Errorf("failed to delete statefulset : %v", retryErr)
	}

	retryErr = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Delete(headless)
	})

	if retryErr != nil && !errors.IsNotFound(retryErr) {
		return fmt.Errorf("failed to delete statefulset headless service : %v", retryErr)
	}

	retryErr = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Delete(head)
	})

	retryErr = retry.RetryOnConflict(retry.DefaultRetry, func() error {
		return sdk.Delete(srt)
	})

	if retryErr != nil && !errors.IsNotFound(retryErr) {
		return fmt.Errorf("failed to delete statefulset head service : %v", retryErr)
	}

	if cluster.Spec.CMName == "" {
		cm := configmap.New(cluster)
		retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
			return sdk.Delete(cm)
		})

		if retryErr != nil && !errors.IsNotFound(retryErr) {
			return fmt.Errorf("failed to delete mariadb cluster default configmap : %v", retryErr)
		}
	}
	return nil
}

// Restart restart mariadb cluster
func Restart(clusterName, namespace, restartPolicy string, ss *appsv1.StatefulSet) error {
	switch restartPolicy {
	case constants.ParallelRestartPolicy:
		listOptions := metav1.ListOptions{LabelSelector: fmt.Sprintf("%v=%v", constants.ClusterLabel, clusterName)}
		retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
			return k8sclient.GetKubeClient().CoreV1().Pods(namespace).DeleteCollection(&metav1.DeleteOptions{}, listOptions)
		})
		if retryErr != nil {
			return retryErr
		}

	case constants.OrderRestartPolicy:
		restartCount := parse.StringToInt(ss.ObjectMeta.Labels[constants.RestartCountKey])
		ss.ObjectMeta.Labels[constants.RestartCountKey] = fmt.Sprintf("%v", restartCount+1)
		retryErr := retry.RetryOnConflict(retry.DefaultRetry, func() error {
			_, err := k8sclient.GetKubeClient().AppsV1().StatefulSets(namespace).Update(ss)
			return err
		})
		if retryErr != nil {
			return retryErr
		}
	default:
	}
	return nil
}

// Exist cluster exist or not
func Exist(cluster *v1alpha1.MariaDBCluster) bool {
	ss, _ := k8sclient.GetKubeClient().AppsV1().StatefulSets(cluster.Namespace).Get(cluster.Name, metav1.GetOptions{})
	if ss.Name != "" {
		return true
	}
	return false
}

// NeedUpdate assert mariadb cluster need to update or not
func NeedUpdate(cluster *v1alpha1.MariaDBCluster) bool {
	cpu := cluster.Spec.Resources.Requests.Cpu().String()
	memory := cluster.Spec.Resources.Requests.Memory().String()
	ss, _ := k8sclient.GetKubeClient().AppsV1().StatefulSets(cluster.Namespace).Get(cluster.Name, metav1.GetOptions{})
	if *ss.Spec.Replicas != cluster.Spec.Replicas || ss.Annotations["cpu"] != cpu || ss.Annotations["memory"] != memory {
		return true
	}
	return false
}
